package com.hidquat;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.io.InputStream;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private FrameLayout stlViewLayout;
    private STLView stlView = null;
    private Handler stlHandler = null;

    private static final String TAG = "MissileLauncherActivity";
    private TextView mText;
    private UsbDevice mUsbDevice;
    private UsbManager mManager;
    private UsbInterface mInterface;
    private UsbDeviceConnection mDeviceConnection;
    private UsbEndpoint mEndpointIn;
    private UsbEndpoint mEndpointOut;
    private static final int deviceVID = 0xABCD;//0x0483;
    private static final int devicePID = 0x0001;//0x5750;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    PendingIntent mPermissionIntent;

    private STLObject stlObject;

    private Quaternion quat = new Quaternion(1.0f, 0f, 0f, 0f);
    private Quaternion quat_rotate = new Quaternion(1.0f, 0f, 0f, 0f);
    private double[] euler = new double[3];

    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                DisplayToast(""+msg.obj);
            }
            if(msg.what==2){
                DisplayData(""+msg.obj);
            }
        };
    };
    private UsbThread usbThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN ,
                WindowManager.LayoutParams. FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);
        mManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        mText = (TextView)findViewById(R.id.mText);

        Button rButton = (Button) findViewById(R.id.resetButton);
        rButton.setOnClickListener(buttonListener);

        /* register broadcast for USB permission*/
        IntentFilter usbPersssionfilter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(mUsbPermissionActionReceiver, usbPersssionfilter);

        /* register broadcast for USB connection*/
        IntentFilter usbDeviceStateFilter = new IntentFilter();
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        registerReceiver(mUsbConnectActionReceiver, usbDeviceStateFilter);

        //UsbEnumerate();

        getSTLBytes();

        stlViewLayout = (FrameLayout)findViewById(R.id.stlFrameLayout);
        stlView = new STLView(this, stlObject);
        stlViewLayout.addView(stlView);
        stlHandler = stlView.getHandler();
    }

    private final BroadcastReceiver mUsbPermissionActionReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        //user choose YES for your previously popup window asking for grant perssion for this usb device
                        openUsbDevice();
                    }
                    else {
                        //user choose NO for your previously popup window asking for grant perssion for this usb device
                        Toast.makeText(context, String.valueOf("Permission denied for device" + usbDevice), Toast.LENGTH_LONG).show();
                    }
                }
            }
        }
    };

    private final BroadcastReceiver mUsbConnectActionReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                DisplayToast("Usb detached!");
                threadAlive = false;
/*                if(usbThread!=null ) {
                    usbThread.resum();
                }
                usbThread = null;*/
            }else if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)){
                UsbEnumerate();
            }
        }
    };

    private void openUsbDevice(){
        //now follow line will NOT show: User has not given permission to device UsbDevice
        UsbDeviceConnection connection = mManager.openDevice(mUsbDevice);
        mDeviceConnection = connection;

        //add your operation code here
        if (mDeviceConnection == null) {
            return;
        }

        // TODO: 2017/4/17 enumerate all interface
        for (int i = 0; i < mUsbDevice.getInterfaceCount();) {
            UsbInterface intf = mUsbDevice.getInterface(i);
            //DisplayToast(i + " " + intf);
            mInterface = intf;
            break;
        }

        // TODO: 2017/4/17 claim all endpoint
        if(mInterface != null && connection.claimInterface(mInterface, true) ) {
            getEndpoint(mInterface);
        }
    }

    public void UsbEnumerate()
    {
        /*check usb device*/
        HashMap<String, UsbDevice> deviceList = mManager.getDeviceList();
        Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
        ArrayList<String> USBDeviceList = new ArrayList<String>();

        while (deviceIterator.hasNext()) {
            UsbDevice device = deviceIterator.next();
            USBDeviceList.add(String.valueOf(device.getVendorId()));
            USBDeviceList.add(String.valueOf(device.getProductId()));
            if (device.getVendorId() == deviceVID && device.getProductId() == devicePID) {
                mUsbDevice = device;
                break;
            }
        }
        if(mUsbDevice == null) {
            DisplayToast("Device not found!");
            usbThread = null;
        } else {
            //DisplayToast("Found device VID:0x" + String.format("%04x", deviceVID) + " PID:0x" + Integer.toHexString( devicePID ));
            //usbThread = new UsbThread(mManager, mUsbDevice, handler);
            //usbThread.start();
            if(mManager.hasPermission(mUsbDevice)){
                openUsbDevice();
            }else{
                mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
                //this line will let android popup window, ask user whether to allow this app to have permission to operate this usb device
                mManager.requestPermission(mUsbDevice, mPermissionIntent);
            }
        }
    }

    // 用UsbDeviceConnection 与 UsbInterface 进行端点设置和通讯
    private void getEndpoint(UsbInterface intf) {
        for(int i=0; i<intf.getEndpointCount(); i++) {
            if (intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) {
                mEndpointIn = intf.getEndpoint(i);
            }else if(intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_OUT) {
                mEndpointOut = intf.getEndpoint(i);
            }
        }

        if(mEndpointIn != null) {
            threadAlive = true;
            if(!usbDataThread.isAlive()) {
                usbDataThread.start();
            }
        }
    }

    /*thread for read data*/
    private boolean threadAlive = false;
    private Thread usbDataThread = new Thread(){
        byte[] Receivebytes = new byte[24];
        @Override
        public void run(){
            while(mEndpointIn != null){
                try {
                    sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if(threadAlive) {
                    mDeviceConnection.bulkTransfer(mEndpointIn, Receivebytes, Receivebytes.length, 100);
                    //display received datas
                    DecimalFormat decimalFormat1=new DecimalFormat("+0.0000;-");
                    DecimalFormat decimalFormat2=new DecimalFormat("+0.0;-");

                    double q0, q1, q2, q3;
                    int tmp = (Receivebytes[1]& 0xFF)*256 + (Receivebytes[0]& 0xFF);
                    q0 = (tmp > 0x8000) ? -(0xffff - tmp) / 16384.0 : tmp / 16384.0;

                    tmp = (Receivebytes[3]& 0xFF)*256 + (Receivebytes[2]& 0xFF);
                    q1 = (tmp > 0x8000) ? -(0xffff - tmp) / 16384.0 : tmp / 16384.0;

                    tmp = (Receivebytes[5]& 0xFF)*256 + (Receivebytes[4]& 0xFF);
                    q2 = (tmp > 0x8000) ? -(0xffff - tmp) / 16384.0 : tmp / 16384.0;

                    tmp = (Receivebytes[7]& 0xFF)*256 + (Receivebytes[6]& 0xFF);
                    q3 = (tmp > 0x8000) ? -(0xffff - tmp) / 16384.0 : tmp / 16384.0;

                    quat.refresh(q0, q1, q2, q3);
                    quat.normal();

                    Quaternion quat_tmp = new Quaternion(quat_rotate);
                    quat_tmp.multi(quat);
                    quat_tmp.getEulerDegree(euler);

                    /*display quaternion*/
                    Message message = Message.obtain();
                    String str = "Q0:" + decimalFormat1.format(quat_tmp.q0) + "  " +
                                 "Q1:" + decimalFormat1.format(quat_tmp.q1) + "  " +
                                 "Q2:" + decimalFormat1.format(quat_tmp.q2) + "  " +
                                 "Q3:" + decimalFormat1.format(quat_tmp.q3) + "\n" +
                                 "X:"  + decimalFormat2.format(euler[0]) + "  "  +
                                 "Y:"  + decimalFormat2.format(euler[1]) + "  "  +
                                 "Z:"  + decimalFormat2.format(euler[2]);
                    message.obj = str;
                    message.what = 2;
                    handler.sendMessage(message);

                    /*send message to stl viewer*/
                    Message messageStl = Message.obtain();
                    messageStl.what = 1;
                    Bundle bundle = new Bundle();
                    bundle.putFloat("q0",(float)quat_tmp.q0);
                    bundle.putFloat("q1",(float)quat_tmp.q1);
                    bundle.putFloat("q2",(float)quat_tmp.q2);
                    bundle.putFloat("q3",(float)quat_tmp.q3);
                    messageStl.setData(bundle);

                    if(stlHandler != null) {
                        stlHandler.sendMessage(messageStl);
                    }
                }
            }
        }
    };

    /* reset button callback function */
    Button.OnClickListener buttonListener = new Button.OnClickListener(){
        public void onClick(View v){
            double[] x_b = new double[3];
            x_b[1] = 1.0f;
            quat.transformVectorBodyToEarth(x_b);
            //use z axis to calculate angle in the case of y axis is close to vertical
            if(Math.abs(x_b[2]) > 0.9) {
                x_b[0] = 0;
                x_b[1] = 0;
                x_b[2] = 1;
                quat.transformVectorBodyToEarth(x_b);
            }
            double angle = Math.atan2(x_b[0], x_b[1]);
            //DisplayToast(Math.toDegrees(angle)+"");
            quat_rotate = new Quaternion(0, 0, angle);
        }
    };

    @Override
    protected void onResume() {
       if(getRequestedOrientation()!=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT/*SCREEN_ORIENTATION_LANDSCAPE*/);
        }
        super.onResume();
        stlView.onResume();
    }
    @Override
    protected void onPause()
    {
        super.onPause();
        stlView.onPause();
    }

    public void DisplayToast(CharSequence str) {
        Toast toast = Toast.makeText(this, str, Toast.LENGTH_SHORT);
        toast.setGravity(Gravity.BOTTOM, 0, 100);
        toast.show();
    }

    public void DisplayData(CharSequence str){
        mText.setText(str);
    }

    /*call back function for reading stl object*/
    class FinishSTL implements STLObject.IFinishCallBack {

        @Override
        public void readstlfinish() {
            //DisplayToast("read stl obj finished");
            UsbEnumerate();
            if(stlView != null){
                stlView.requestRender();
            }
        }
    }

    /* get stl bytes from raw file */
    private void getSTLBytes() {
        byte[] stlBytes = null;
        InputStream inputStream;
        try {
            inputStream = getResources().openRawResource(R.raw.helicopter);
            stlBytes = new byte[inputStream.available()];
            inputStream.read(stlBytes);
            inputStream.close();
        }catch(Exception e){
        }

        if(stlBytes == null) {
            DisplayToast(getString(R.string.error_fetch_data));
            return;
        }

        stlObject = new STLObject(stlBytes, this, new FinishSTL());
    }
}
